package com.Vronsky.core.dataSource;

import com.Vronsky.config.DataSourceProperties;
import com.Vronsky.core.connection.DecorateConnection;
import com.Vronsky.core.exception.VronskyCommonException;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Logger;

//数据源对象
public class SimpleDataSource implements DataSource {
    //记录创建过的连接总数量
    private  Integer totalConn=0;

    //这里会有线程安全问题，待优化
    private Queue<DecorateConnection> connPool = new ConcurrentLinkedQueue<DecorateConnection>();
    private static Integer initializationConnNum;
    private static String url;
    private static String user;
    private static String password;
    private static long timeOut;
    private static SimpleDataSource simpleDataSource;

    static {

    }

    public Queue<DecorateConnection> getConnPool() {
        return connPool;
    }

    //类被加载时先加载数据源信息dataSourceProperties
    static {
//          Class.forName("com.Vronsky.config.DataSourceProperties");  //这一步可以省略
            DataSourceProperties dataSourceProperties=DataSourceProperties.getDataSourceProperties();
            initializationConnNum=dataSourceProperties.getInitializationConnNum();
            url=dataSourceProperties.getUrl();
            user=dataSourceProperties.getUser();
            password=dataSourceProperties.getPassword();
            timeOut=dataSourceProperties.getTimeOut();
        try {
            simpleDataSource = new SimpleDataSource();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }


    //往连接池添加连接
    private DecorateConnection addOneConn() throws SQLException {

            java.sql.Connection conn = DriverManager.getConnection(url, user, password);
            DecorateConnection decorateConnection = new DecorateConnection();
            decorateConnection.setConnection(conn);
            connPool.add(decorateConnection);
            totalConn++;

        return decorateConnection;
    }
    public void setConnPool(LinkedList<DecorateConnection> connPool) {
        this.connPool = connPool;
    }
    //连接池队列


    /**
     * 不知道怎么保持原方法名getConnection的情况下返回com.Vronsky.core.connection.Connection
     * 无奈将将方法名getConnection改成getConn,
     * @return
     * @throws SQLException
     */
    public com.Vronsky.core.connection.Connection getConn() throws SQLException {
        DataSourceProperties dataSourceProperties = DataSourceProperties.getDataSourceProperties();
        Integer maxConnNum = dataSourceProperties.getMaxConnNum();
        DecorateConnection connection=null;
        // 先尝试从 connPool 中获取

            if(connPool.size()==0 && totalConn==maxConnNum){
                //等待一秒
                 long totalWaitTime=timeOut;

                try {
                    if(totalWaitTime>0){

                        while (connPool.size()==0){
                            long startTime=System.currentTimeMillis();
                            //这里加synchronized (this)是为了让当前线程使用simpleDataSource作为锁对象
                            synchronized (this) {
                                this.wait(totalWaitTime);
                            }
                            long endTime=System.currentTimeMillis();
                            long usedTime=endTime-startTime;
                            totalWaitTime-=usedTime;
                        }
                            connection = connPool.poll();
                            return connection;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }catch (IllegalArgumentException e) {
                    throw new VronskyCommonException("已无空闲连接");
                }
            }

            if (connPool.size()>0){
                connection = connPool.poll();
                return connection;
            }

            if(connPool.size()==0 && totalConn<maxConnNum){
                connection = addOneConn();
                connPool.poll();
                return connection;
            }

        throw new VronskyCommonException("未知异常");
    }

    public static SimpleDataSource getSimpleDataSource() throws SQLException, ClassNotFoundException {
        return simpleDataSource;
    }

    /**
     * 当创建数据源对象会自动创建初始数量的连接
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    private SimpleDataSource() throws ClassNotFoundException, SQLException {
        /**
         * 可以完善的点，用户用URL，username,password可以取得数据源
         * 也可以用host port database username  password取得数据源
         */
        //创建初始数量的连接放入连接池

        for (int i = 0; i < initializationConnNum; i++) {
            java.sql.Connection conn = DriverManager.getConnection(url, user, password);
            DecorateConnection decorateConnection = new DecorateConnection();
            decorateConnection.setConnection(conn);
            connPool.add(decorateConnection);
            totalConn++;
        }
    }


    @Override
    public Connection getConnection() throws SQLException {
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}
